`timescale 1ns / 1ps
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Company:			Microsystems Design Lab (MDL)
//					The Pennsylvania State University
// Engineer: 		Yang Xiao
// 
// Create Date:		10/23/2012 
// Design Name: 	ramfifo
// Module Name:     ramfifo
// Project Name:	ISCA-2013
// Target Devices: 
// Tool versions: 
// Description:		A generic BRAM storage module that can be used RAM or FIFO based on the configuration	
//					
//			
//			
//		
//		
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module ramfifo(
	//----------------------------
	// Inputs
	//----------------------------
	clk					,
	config_en			,  				// --- config enable signal ---
	config_mode			,  				// --- config mode: 0 means RAM, 1 means FIFO ---

	ram_dina			,  				// --- RAM data input port A ---
	ram_addra			,  				// --- RAM addr input port A ---
	ram_wea				,  				// --- RAM wren port A ---
	ram_dinb			,  				// --- RAM data input port B ---
	ram_addrb			,  				// --- RAM addr input port B ---
	ram_web				,				// --- RAM wren port B ---

	fifo_wr_clk			,				// --- FIFO write clock ---
	fifo_rd_clk			,				// --- FIFO read clock ---
	fifo_din			,				// --- FIFO data input ---
	fifo_wren			,				// --- FIFO write enable ---
	fifo_rden			,				// --- FIFO read enable ---

	//----------------------------
	// Outputs
	//----------------------------
	ram_douta			,				// --- RAM data output port A ---
	ram_doutb			,				// --- RAM data output port B ---
	
	fifo_dout			,				// --- FIFO data out ---
	fifo_full			,				// --- FIFO full signal ---
	fifo_empty			,				// --- FIFO empty signal ---
	fifo_almost_full	,				// --- FIFO almost full signal ---
	fifo_almost_empty					// --- FIFO almost empty signal ---
);

	//--------------------------
	// Parameters
	//--------------------------
	parameter	DATA_WIDTH	= 32	;
	parameter	ADDR_DEPTH	= 4	;
	parameter 	ADDR_WIDTH	= 	(ADDR_DEPTH <= 2)    ? 1  :
								(ADDR_DEPTH <= 4)    ? 2  :
								(ADDR_DEPTH <= 8)    ? 3  :
								(ADDR_DEPTH <= 16)   ? 4  :
								(ADDR_DEPTH <= 32)   ? 5  :
								(ADDR_DEPTH <= 64)   ? 6  :
								(ADDR_DEPTH <= 128)  ? 7  :
								(ADDR_DEPTH <= 256)  ? 8  :
								(ADDR_DEPTH <= 512)  ? 9  :
								(ADDR_DEPTH <= 1024) ? 10 :
								(ADDR_DEPTH <= 2048) ? 11 :
								(ADDR_DEPTH <= 4096) ? 12 :
								(ADDR_DEPTH <= 8192) ? 13 : 14;
		
	//--------------------------
	// Input Ports
	//--------------------------
	input							clk					;
	input							config_en			;
	input							config_mode			;

	input	[DATA_WIDTH-1 : 0]		ram_dina			;
	input	[ADDR_WIDTH-1 : 0]		ram_addra			;
	input							ram_wea				;
	input	[DATA_WIDTH-1 : 0]		ram_dinb			;
	input	[ADDR_WIDTH-1 : 0]		ram_addrb			;
	input							ram_web				;

	input							fifo_wr_clk			;
	input							fifo_rd_clk			;
	input	[DATA_WIDTH-1 : 0]		fifo_din   			;
	input							fifo_wren  			;
	input							fifo_rden  			;
	//--------------------------
	// Output Ports
	//--------------------------
	output	[DATA_WIDTH-1 : 0]		ram_douta			;
	output	[DATA_WIDTH-1 : 0]		ram_doutb			;

	output	[DATA_WIDTH-1 : 0]		fifo_dout			;
	output							fifo_full			;
	output							fifo_empty			;
	output							fifo_almost_full	;
	output							fifo_almost_empty	;
	
	//--------------------------
	// Bidirectional Ports
	//--------------------------
	// < Enter Bidirectional Ports in Alphabetical Order >
	
	
	///////////////////////////////////////////////////////////////////
	// Begin Design
	///////////////////////////////////////////////////////////////////
	
	//-------------------------------------------------
	// Signal Declarations: reg
	//-------------------------------------------------
	reg								config_mode_r		= 0;			// --- register to store the current working status ---
	reg		[ADDR_WIDTH-1 : 0]		wr_pointer_r		= 0;			// --- write pointer register ---
	reg		[ADDR_WIDTH : 0]		fifo_counter_r		= 0;			// --- write counter register ---
	reg		[ADDR_WIDTH-1 : 0]		rd_pointer_r		= 0;			// --- read pointer register ---
	reg								fifo_empty_r		= 1;			// --- fifo empty register ---
	reg								fifo_almost_empty_r	= 1;			// --- fifo almost empty register ---
	
	//-------------------------------------------------
	// Signal Declarations: wire
	//-------------------------------------------------
	wire	[DATA_WIDTH-1 : 0]		bram_dina_w	;
	wire	[ADDR_WIDTH-1 : 0]		bram_addra_w;
	wire							bram_wea_w	;	
	wire	[DATA_WIDTH-1 : 0]		bram_douta_w;
	wire	[DATA_WIDTH-1 : 0]		bram_dinb_w	;
	wire	[ADDR_WIDTH-1 : 0]		bram_addrb_w;
	wire							bram_web_w	; 
	wire	[DATA_WIDTH-1 : 0]		bram_doutb_w;

    //---------------------------------------------------------------
	// Assignments
	//---------------------------------------------------------------
	assign bram_dina_w	= (config_mode_r) ? fifo_din : ram_dina;
	assign bram_addra_w	= (config_mode_r) ? wr_pointer_r : ram_addra;
	assign bram_wea_w	= (config_mode_r) ? (fifo_wren && fifo_counter_r < ADDR_DEPTH) : ram_wea;
	assign ram_douta	= bram_douta_w;

	assign bram_dinb_w	= ram_dinb;
	assign bram_addrb_w	= (config_mode_r) ? rd_pointer_r : ram_addrb;
	assign bram_web_w	= ram_web	;
	assign ram_doutb	= bram_doutb_w;
	assign fifo_dout	= bram_doutb_w;

	assign fifo_full			= (fifo_counter_r == ADDR_DEPTH);
	assign fifo_empty			= fifo_empty_r;
	assign fifo_almost_full		= (fifo_counter_r == ADDR_DEPTH - 1);
	assign fifo_almost_empty	= fifo_almost_empty_r;
 
	
    //---------------------------------------------------------------
	// Instantiations
	//---------------------------------------------------------------
	dp_bram  #(
		.WIDTH(DATA_WIDTH),
		.N_DEPTH(ADDR_DEPTH),
		.W_DEPTH(ADDR_WIDTH)
	)
	dp_bram_inst(
		.clka	(clk),
		.dina	(bram_dina_w),
		.addra	(bram_addra_w),
		.wea	(bram_wea_w),
		.douta	(bram_douta_w),

		.clkb	(clk),
		.dinb	(bram_dinb_w),
		.addrb	(bram_addrb_w),
		.web	(bram_web_w),
		.doutb	(bram_doutb_w)	
	);

	//---------------------------------------------------------------
	// Combinatorial Logic
	//---------------------------------------------------------------
	always @(posedge clk) begin
		// Default register setting
		config_mode_r				<= config_mode_r;
		wr_pointer_r				<= wr_pointer_r;
		rd_pointer_r				<= rd_pointer_r;
		config_mode_r				<= config_mode_r;
		fifo_counter_r				<= fifo_counter_r;
		fifo_empty_r				<= (fifo_counter_r == 0);
		fifo_almost_empty_r			<= (fifo_counter_r == 1);

		// set the mode
		if (config_en) begin
			config_mode_r 			<= config_mode;
		end

		// fifo write & read counter
		if (config_mode_r) begin
			// write pointer
			if (fifo_wren && fifo_counter_r < ADDR_DEPTH) begin
				if (wr_pointer_r == ADDR_DEPTH-1) begin
					wr_pointer_r	<= 0;
				end
				else begin
					wr_pointer_r	<= wr_pointer_r + 1;
				end
			end

			// read pointer
			if (fifo_rden && fifo_counter_r > 0) begin
				if (rd_pointer_r == ADDR_DEPTH-1) begin
					rd_pointer_r	<= 0;
				end
				else begin
					rd_pointer_r	<= rd_pointer_r + 1;
				end
			end

			// fifo counter
			if (fifo_wren && fifo_rden) begin
				fifo_counter_r		<= fifo_counter_r;
			end
			else if (fifo_wren && fifo_counter_r < ADDR_DEPTH) begin
				fifo_counter_r		<= fifo_counter_r + 1;
			end
			else if (fifo_rden && fifo_counter_r > 0) begin
				fifo_counter_r		<= fifo_counter_r - 1;
			end

		end

		/*
		case ()
			IDLE: begin
			end
			
			default: begin
			end			
		endcase
		*/
	end
	
	
	
	//---------------------------------------------------------------
	// Sequential Logic
	//---------------------------------------------------------------

endmodule 







